<template>
  <div ref="tabsWrapper_ref" class="custom-tabs-wrapper" :class="scrollable && 'tabs-nav-scrollable'">
    <button
      class="button-empty-style tab-nav-prev"
      :class="[scrollable ? '' : 'tab-nav-disabled', prevDisabled ? 'btn-disabled' : '']"
      @click="scrollPrev"
    >
      <Icon type="ios-arrow-back"></Icon>
    </button>
    <button
      class="button-empty-style tab-nav-next"
      :class="[scrollable ? '' : 'tab-nav-disabled', nextDisabled ? 'btn-disabled' : '']"
      @click="scrollNext"
    >
      <Icon type="ios-arrow-forward"></Icon>
    </button>
    <div ref="tabsScroll_ref" class="tabs-scroll">
      <ul class="custom-tabs" ref="custom-tabs" :style="navStyle">
        <li
          v-for="(item, i) of tabs"
          :key="i"
          class="tab-item pointer"
          :class="[{ 'tab-active': activeKey === item.value, 'tab-disabled': item.disabled }]"
          @click="handleTabItemClick(item, i)"
        >
          <slot :item="item">
            <span class="role-item--label">{{ item.label }}</span>
          </slot>
        </li>
        <li class="tabs-bar" :style="barStyle"></li>
      </ul>
    </div>
  </div>
</template>

<script>
const strMatchNumber = (str) => Number(str.match(/\d+/)[0])
export default {
  name: 'CustomTabs',

  props: {
    value: {
      type: [String, Number],
    },

    tabs: {
      type: Array,
    },

    bindValField: {
      default: 'value',
    },

    barHeight: {
      default: '2px',
    },
  },

  data() {
    return {
      init: false,
      barWidth: 0,
      barOffset: 0,
      activeKey: this.value,
      focusedKey: this.value,
      navStyle: {
        transform: '',
      },
      scrollable: false,
      prevDisabled: true,
      nextDisabled: false,
    }
  },

  computed: {
    barStyle() {
      return {
        width: this.barWidth + 'px',
        height: this.barHeight,
        transform: `translateX(${this.barOffset}px)`,
      }
    },

    activeIndex() {
      return this.tabs.findIndex((it) => it.value === this.activeKey)
    },
  },

  watch: {
    value(val) {
      this.activeKey = val
      this.focusedKey = val
    },

    activeKey() {
      this.updateBar()
      this.$nextTick(() => this.scrollToActiveTab())
    },

    'navStyle.transform'(v) {
      const currentOffset = this.getCurrentScrollOffset()
      const navWidth = this.$refs['custom-tabs'].offsetWidth
      const tabsScroll = this.$refs['tabsScroll_ref'].offsetWidth

      if (currentOffset <= 0) {
        this.prevDisabled = true
      } else {
        this.prevDisabled = false
      }

      if (currentOffset === navWidth - tabsScroll) {
        this.nextDisabled = true
      } else {
        this.nextDisabled = false
      }
    },

    tabs(v) {
      if (!this.init) return
      this.updateBar()
    },
  },

  methods: {
    handleTabItemClick(tab, i) {
      if (tab.disabled) return
      this.activeKey = tab[this.bindValField]
      this.$emit('click', tab, i)
    },

    async updateBar() {
      await this.$nextTick()
      const tabItems = this.$refs['custom-tabs'].querySelectorAll('.tab-item')
      const currentTab = tabItems[this.activeIndex]
      const rect = window.getComputedStyle(currentTab)
      this.barWidth = strMatchNumber(rect.width)
      const marginLeft = strMatchNumber(rect['margin-left'])

      if (this.activeIndex > 0) {
        let offset = 0
        // const styleList = ['width', 'margin-left', 'margin-right', 'padding-left', 'padding-right']
        const styleList = ['width', 'margin-left', 'margin-right']
        for (let i = 0; i < this.activeIndex; i++) {
          const style = window.getComputedStyle(tabItems[i])
          styleList.forEach((k) => {
            offset += strMatchNumber(style[k])
          })
        }
        this.barOffset = offset + marginLeft
      } else {
        this.barOffset = 0 + marginLeft
      }

      this.updateNavScroll()
    },

    updateNavScroll() {
      const tabsScroll = this.$refs['tabsScroll_ref'].offsetWidth
      const tabsWidth = this.$refs['custom-tabs'].offsetWidth
      const currentOffset = this.getCurrentScrollOffset()

      if (tabsWidth > tabsScroll) {
        this.scrollable = true
        if (tabsWidth - currentOffset < tabsScroll) {
          this.setNavStyle(tabsWidth - tabsScroll)
        }
      } else {
        this.scrollable = false
        if (currentOffset > 0) {
          this.setNavStyle(0)
        }
      }
      this.init = true
    },

    scrollPrev() {
      const tabsScroll = this.$refs['tabsScroll_ref'].offsetWidth
      const currentOffset = this.getCurrentScrollOffset()

      if (!currentOffset) {
        this.return
      }

      let newOffset = currentOffset > tabsScroll ? currentOffset - tabsScroll : 0

      this.setNavStyle(newOffset)
    },

    scrollNext() {
      const navWidth = this.$refs['custom-tabs'].offsetWidth
      const tabsScroll = this.$refs['tabsScroll_ref'].offsetWidth
      const currentOffset = this.getCurrentScrollOffset()
      if (navWidth - currentOffset <= tabsScroll) {
        return
      }

      let newOffset =
        navWidth - currentOffset > tabsScroll * 2 ? currentOffset + tabsScroll : navWidth - tabsScroll

      this.setNavStyle(newOffset)
    },

    getCurrentScrollOffset() {
      const { navStyle } = this
      return navStyle.transform ? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1]) : 0
    },

    setNavStyle(value) {
      this.navStyle.transform = `translateX(-${value}px)`
    },

    scrollToActiveTab() {
      if (!this.scrollable) return
      const tab = this.$refs['custom-tabs']
      const activeTab = this.$el.querySelector(`.tab-active`)
      if (!activeTab) return

      const tabsWrapper = this.$refs['tabsWrapper_ref']
      const activeTabBounding = activeTab.getBoundingClientRect()
      const activeTabStyle = window.getComputedStyle(activeTab)
      const atPl = strMatchNumber(activeTabStyle.paddingLeft)
      const atPr = strMatchNumber(activeTabStyle.paddingRight)
      const atMl = strMatchNumber(activeTabStyle.marginLeft)
      const atMr = strMatchNumber(activeTabStyle.marginRight)
      const activeTabGap = atPl + atPr + atMl + atMr

      const tabsWrapperBounding = tabsWrapper.getBoundingClientRect()
      const tabBounding = tab.getBoundingClientRect()
      const currentOffset = this.getCurrentScrollOffset()
      let newOffset = currentOffset

      if (tabBounding.right < tabsWrapperBounding.right) {
        newOffset = tabBounding.offsetWidth - tabsWrapperBounding.width
      }

      if (activeTabBounding.left - activeTabGap < tabsWrapperBounding.left) {
        newOffset = currentOffset - (tabsWrapperBounding.left - activeTabBounding.left + activeTabGap)
      } else if (activeTabBounding.right > tabsWrapperBounding.right) {
        newOffset = currentOffset + activeTabBounding.right - tabsWrapperBounding.right + activeTabGap
      }

      if (currentOffset !== newOffset) {
        this.setNavStyle(Math.max(newOffset, 0))
      }
    },
  },
}
</script>

<style lang="less" scoped>
.custom-tabs-wrapper {
  position: relative;
  overflow: hidden;

  .tab-nav-next,
  .tab-nav-prev {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 32px;
    // height: 100%;
    // line-height: px;
    text-align: center;
    cursor: pointer;
    // background-color: #fff;
  }

  .tab-nav-prev {
    left: 0;
  }
  .tab-nav-next {
    right: 0;
  }

  .tab-nav-disabled {
    display: none;
  }
}

.tabs-scroll {
  overflow: hidden;
  white-space: nowrap;
}

.custom-tabs {
  display: flex;
  position: relative;
  float: left;
  transition: transform 0.4s ease-in-out;
}

.tabs-nav-scrollable {
  padding: 0 32px;
}

.tab-item {
  flex-shrink: 0;
  padding: 4px 0;
}

.tab-item.tab-active {
  color: var(--app-text-active);
}

.tabs-bar {
  position: absolute;
  bottom: 0;
  background-color: var(--app-text-active);
  transition: transform 0.4s;
}

.btn-disabled {
  // cursor: not-allowed !important;
  opacity: 0.4;
}
</style>
